﻿using System.Collections.Generic;
namespace GX
{

    public class Entity : IComparer<Behaviour>
    {
        Dictionary<int, Component> _componentMap = new Dictionary<int, Component>();

        Dictionary<int, Behaviour> _bevMap = new Dictionary<int, Behaviour>();
        List<Behaviour> _bevList = new List<Behaviour>();
        int _flag;

        //遍历深度 bit[0,7]:0xff -> 1111 1111
        int traverseDepth {
            get { return (_flag & 0xff); }
            set { BitHelper.WriteMask(ref _flag, value, 0xff); }
        }
        //[8,23]:0xffff00 -> 1111 1111 1111 1111 0000 0000
        int firstEmptyIndex {
            get { return (_flag & 0xffff00) >> 8 ; }
            set { BitHelper.WriteMask(ref _flag, value<<8, 0xffff00); }
        }
        //[24]:0x1000000 -> 0001 0000 0000 0000 0000 0000 0000
        bool isWaitingDestroy
        {
            get { return (_flag & 0x1000000) != 0; }
            set { BitHelper.WriteMask(ref _flag, value ? 0x1000000 : 0, 0x1000000); }
        }
        //[25]:0x1000000 -> 0010 0000 0000 0000 0000 0000 0000
        bool isDestroying
        {
            get { return (_flag & 0x2000000) != 0; }
            set { BitHelper.WriteMask(ref _flag, value ? 0x2000000 : 0, 0x2000000); }
        }
        public bool AddComponent(int id, Component component)
        {
            if (null != component.entity || _componentMap.ContainsKey(id) || this.isDestroying)
                return false;
            _componentMap[id] = component;
            component.SetEntity(this);
            component.OnAttach();
            return true;
        }
        public Component GetComponent(int id)
        {
            Component component=null;
            if (_componentMap.TryGetValue(id, out component) 
                && component.entity == this)
                return component;
            return null;
        }

        public bool AddBehaviour(Behaviour behaviour)
        {
            if (null != behaviour.entity
                || this.isDestroying
                || _bevMap.ContainsKey(behaviour.id))
                return false;

            int index;
            if (!ListSearch.BinarySearch(_bevList,behaviour, this,out index))
                _bevList.Insert(index, behaviour);

            behaviour.SetEntity(this);
            _bevMap[behaviour.id] = behaviour;
            behaviour.OnAttach();
            return true;
        }
        public Behaviour RemoveBehaviour(int id)
        {
            Behaviour behaviour=null;
            if (_bevMap.TryGetValue(id, out behaviour))
            {
                behaviour.SetEntity(null);
                _bevMap.Remove(id);

                int index;

                ListSearch.BinarySearch(_bevList, behaviour, this, out index);

                if (0 == this.traverseDepth)
                    _bevList.RemoveAt(index);
                else
                {
                    var curEmptyIndex = this.firstEmptyIndex;
                    if (curEmptyIndex == 0 || index <= curEmptyIndex)
                        this.firstEmptyIndex = index + 1;
                }

                behaviour.OnDetach(this,false);
            }
            return behaviour;
        }
        public Behaviour GetBehaviour(int id)
        {
            Behaviour behaviour;
            _bevMap.TryGetValue(id, out behaviour);
            return behaviour;
        }
        bool TryGetIndex(Behaviour behaviour, out int index)
        {
            int len = _bevList.Count;

            int L = 0;
            int H = len - 1;

            int pos = -1;
            int compareValue = 1;
           
            Behaviour bev;
            while (L <= H)
            {
                pos = (L + H)/2;
                bev =  _bevList[pos];
                if (bev == behaviour)
                {
                    index = pos;
                    return true;
                }
                compareValue = behaviour.order.CompareTo(bev.order);
                if (0 == compareValue || L == H)
                    break;
                if (compareValue > 0)
                    L = pos + 1;
                else
                    H = pos - 1;
            }
            if (compareValue == 0)
            {
                int i = pos-1;
                for (; i>= L; --i)
                {
                    bev = _bevList[i];
                    if (bev == behaviour)
                    {
                        index = pos;
                        return true;
                    }
                    if (bev.order != behaviour.order)
                        break;
                }
                for (i = pos + 1; i <= H; ++i)
                {
                    bev = _bevList[i];
                    if (_bevList[pos] == behaviour)
                    {
                        index = pos;
                        return true;
                    }
                    if (bev.order == behaviour.order)
                        pos = i;
                    else
                        break;
                }
            }
            if (compareValue >= 0)
                index = pos + 1;
            else
                index = pos;
            return false;
        }
        public int Compare(Behaviour x, Behaviour y)
        {
            if (x.order != y.order)
                return x.order < y.order ? -1 : 1;
            if (x.id != y.id)
                return x.id < y.id ? -1 : 1;
            return 0;
        }
        public void Broadcast(int eventID, object arg)
        {
            ++this.traverseDepth;

            int len = _bevList.Count;

            Behaviour bev;
            for (int i = 0; i < len; ++i)
            {
                bev = _bevList[i];
                if (this == bev.entity)
                    bev.ProcessEvent(eventID, arg);
            }
            --this.traverseDepth;

            if (0 == traverseDepth)
            {
                if (this.isWaitingDestroy)
                {
                    this.isWaitingDestroy = false;
                    DoDestroy();
                }
                Trim();
            }
        }
        public void Dispose()
        {
            if (this.isWaitingDestroy || this.isDestroying)
                return;

            if (0 == this.traverseDepth)
            {
                DoDestroy();
                Trim();
            }else
                this.isWaitingDestroy = true;
        }
        void DoDestroy()
        {
            this.isDestroying = true;

            var iter = _componentMap.GetEnumerator();
            while (iter.MoveNext())
            {
                var comp = iter.Current.Value;
                comp.SetEntity(null);
                comp.OnDetach(this);
            }
            _componentMap.Clear();

            int len = _bevList.Count;

            ++this.traverseDepth;
            
            Behaviour bev;
            for (int i = 0; i < len; ++i)
            {
                bev = _bevList[i];
                if (this == bev.entity)
                {
                    _bevMap.Remove(bev.id);
                    bev.SetEntity(null);
                    bev.OnDetach(this,true);
                }
            }
            _bevList.Clear();
            _bevMap.Clear();
            
            --this.traverseDepth;

            this.isDestroying = false;

        }
        void Trim()
        {
            int index = this.firstEmptyIndex;
            if (0 == index)
                return;
            this.firstEmptyIndex = 0;
            -- index;

            int len = _bevList.Count;
            Behaviour bev;
            for(int i=firstEmptyIndex+1;i < len; ++i)
            {
                bev = _bevList[i];
                if (bev.entity == this)
                {
                    _bevList[index++] = bev;
                }
            }
            _bevList.RemoveRange(index,len - index);
        }
    }
}